/* receiver.c -- Receive updates from the network
 *
 *    Copyright 2010 Patrick Allen
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
*/

#include <inttypes.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

#include "receiver.h"
#include "debug.h"
#include "holddown.h"
#include "advrp.h"
#include "kroute.h"
#include "hash.h"

/* Does neighbor n have the address of our loopback? */
#define LOCAL(n) (n->addr == LOOPBACK_IP)

extern _Bool haveNeighbor;
extern pthread_mutex_t mutexAdvrp;
extern pthread_cond_t condNeighbor;
extern pthread_mutex_t mutexHaveNeighbor;
extern interface * firstInterface;
extern internalRoute * firstInternalRoute, * lastInternalRoute;
extern hdNetwork * firstHdNetwork;
extern neighbor * firstNeighbor, * lastNeighbor, * firstRemoteNeighbor;

void * receiver_loop(void * notused);
int recv_setup_network(struct sockaddr_in * server);
void process_update(updateMessage * msg, struct sockaddr_in * client);
void update_neighbor_routes(updateMessage * msg, neighbor * neigh);
void delete_invalid_routes(neighbor * neigh);
void process_valid_route(neighbor * neigh, neighborRoute * nroute,
                         updateRecord * rec, char * ipstr, uint64_t hash);
void neighbor_route_mod(neighbor * neigh, neighborRoute * nroute,
                        updateRecord * rec, int8_t diff);
_Bool best_neighbor_route_to(uint64_t hash, internalRoute * best);
void neighbor_route_add(neighbor * neigh, neighborRoute * nroute,
                        updateRecord * rec, char * ipstr, uint64_t hash);
void internal_route_del(uint64_t hash);
internalRoute * lookup_internal_route(uint64_t hash);
_Bool is_new_best_route(neighborRoute * new, neighborRoute ** old);
internalRoute * get_internal_route(uint64_t hash);
void internal_route_add(neighbor * neigh, neighborRoute * nr);
neighborRoute * get_last_neighbor_route(neighbor * neigh);
void process_invalid_route(neighbor * neigh, neighborRoute * nroute);
neighborRoute * neighbor_route_lookup(neighbor * neighbor, uint64_t hash);
uint8_t update_metric(updateRecord * rec);
void update_neighbor(neighbor * neigh);
_Bool is_local(in_addr_t addr);
neighbor * lookup_neighbor(in_addr_t addr);
neighbor * create_neighbor(in_addr_t addr, neighbor * prev);
void flush_valid_flags(neighbor * neigh);

/* receiver_loop
 * Called by main when the receive thread is started */
void * receiver_loop(void * notused)
{
	updateMessage msg;
	int fd;
	struct sockaddr_in server, client;
	socklen_t size = (socklen_t) sizeof(client);
	sigset_t newmask;

	DBG(DEV_INOUT, "Entered function.");

	/* Deal with signals */
	newmask.__val[0] = SIGINT;
	newmask.__val[1] = SIGTERM;
	newmask.__val[2] = SIGHUP;
	pthread_sigmask(SIG_SETMASK, &newmask, 0);

	DBG(DEV_1, "Receiver thread id: %u", pthread_self());

	/* Open socket and bind to it */
	if ((fd = recv_setup_network(&server)) == -1)
		cleanup();

	/* Infinite loop to receive messages */
	while (1) {
		/* Clear out the old message */
		memset(&msg, 0, sizeof(struct updateMessage));
		DBG(DEV_2, "Zeroed out msg.");

		/* Wait for a message */
		DBG(RECV_1, "Waiting for an update...");
		recvfrom(fd, &msg, sizeof(msg), 0, (struct sockaddr *) &client,
		         &size);

		/* Got a message, so process it */
		DBG(RECV_1, "Got an update.");
		pthread_mutex_lock(&mutexAdvrp);
		process_update(&msg, &client);
		pthread_mutex_unlock(&mutexAdvrp);
	}
}

int recv_setup_network(struct sockaddr_in * server)
{
	int fd;

	DBG(DEV_INOUT, "Entered function.");

	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		DBG(ERR, "Could not create a socket.");
		return -1;
	} else {
		DBG(DEV_2, "Receive socket created.");
	}
	server->sin_family = AF_INET;
	server->sin_addr.s_addr = htonl(INADDR_ANY);
	server->sin_port = htons(ADVRP_PORT);

	if (bind(fd, (struct sockaddr *) server, sizeof(*server)) == 0) {
		DBG(DEV_2, "Receive bind completed successfully.");
	} else {
		DBG(ERR, "Could not bind to port %d", ADVRP_PORT);
		close(fd);
		return -1;
	}

	return fd;
}

void process_update(updateMessage * msg, struct sockaddr_in * client)
{
	neighbor * neigh;
	in_addr_t addr;
	char ipstr[INET_ADDRSTRLEN];
	int version = ntohl(msg->version);

	DBG(DEV_INOUT, "Entered function.");
	addr = client->sin_addr.s_addr;

	/* We'll hear all broadcasts that *we* send out, so ignore those */
	if (is_local(addr)) {
		DBG(RECV_1, "Ignoring my own update.");
		DBG(DEV_INOUT, "Leaving function.");
		return;
	}
	inet_ntop(AF_INET, &addr, ipstr, sizeof(ipstr));
	DBG(RECV_1, "Received message from %s.", ipstr);

	if ((neigh = lookup_neighbor(addr)) == NULL) {

		/* This is a new neighbor */
		neigh = create_neighbor(addr, lastNeighbor);
	}

	if (version != ADVRP_VERSION) {
		DBG(RECV_1, "Ignoring incompatible version %d update.",
		    version);
		return;
	}

	update_neighbor(neigh);
	update_neighbor_routes(msg, neigh);

	DBG(DEV_INOUT, "Leaving function.");
	return;
}

/* update_neighbor_routes
 * For every record in this update, update our neighbor route entry to match the
 * record. If there are new routes, add them. If previously known routes are
 * not in this update, delete them. Set hold down flags for networks advertised
 * with an infinite distance. Update internal routes appropriately, which
 * will in turn update the kernel.
 */
void update_neighbor_routes(updateMessage * msg, neighbor * neigh)
{
	uint64_t hash = 0;
	uint8_t newmetric;
	uint8_t i;
	char ipstr[INET_ADDRSTRLEN];
	updateRecord * rec;              /* Current record */
	neighborRoute * nroute = NULL;   /* Neighbor route */
	int nrec = ntohl(msg->nrec);

	DBG(DEV_INOUT, "Entered function.");
	DBG(RECV_1, "Number of records in this update: %d", nrec);

	/* Loop through all records in this update */
	for (i = 0; i < nrec; i++) {
		DBG(RECV_2, "Processing route entry %d.", i+1);
		rec = &msg->updateRecord[i];
		if (inet_ntop(AF_INET, &rec->network, ipstr, sizeof(ipstr)) &&
		    rec->preflen <= 32 && rec->preflen >= 0) {
			DBG(RECV_1, "Received update for network: %s/%d"
			    " in %d hop%s.", ipstr, rec->preflen, 
			    rec->metric, rec->metric == 1 ? "" : "s");
		} else {
			DBG(RECV_1, "Received malformed route, ignoring...");
			continue;
		}

		hash = make_hash(rec->network, rec->preflen);
		DBG(DEV_1, "Hash is %llu.", hash);
		nroute = neighbor_route_lookup(neigh, hash);

		if (on_holddown(hash)) {
			DBG(RECV_1, "Prefix is on holddown, ignoring.");
			continue;
		}

		if (rec->metric > MAX_METRIC) {
			/* Route is invalid */
			process_invalid_route(neigh, nroute);
		} else {
			/* Route is valid */
			process_valid_route(neigh, nroute, rec, ipstr, hash);
		}
	}

	/* Done processing this update.  Delete any previously existing routes
	 * that we didn't get an update for (implicitly invalid), then flush 
	 * all routes' valid flags for next time.
	 */
	DBG(RECV_3, "Deleting implicitly invalid routes.");
	delete_invalid_routes(neigh);
	DBG(DEV_1, "Flushing valid flags.");
	flush_valid_flags(neigh);
	DBG(DEV_INOUT, "Leaving function.");
	return;
}

void flush_valid_flags(neighbor * neigh)
{
	neighborRoute * route;

	DBG(DEV_1, "Flushing valid flags.");

	for (route = neigh->firstroute; route != NULL; route = route->next)
		route->valid = 0;
}


/* delete_invalid_routes
 * Delete all routes from this neighbor that do not have the valid flag set.
 */
void delete_invalid_routes(neighbor * neigh)
{
	neighborRoute * route;

	for (route = neigh->firstroute; route != NULL; route = route->next)
		if (! route->valid)
			process_invalid_route(neigh, route);
	return;
}

/* process_valid_route
 * Deal with valid routes. Add/update routes as necessary.
 * Set the valid flag on this route.
 */
void process_valid_route(neighbor * neigh, neighborRoute * nroute,
                         updateRecord * rec, char * ipstr, uint64_t hash)
{
	int8_t diff;            /* difference between old and new metric */

	DBG(DEV_INOUT, "Entered function.");

	/* If route is new, add it, otherwise if the metric has changed update
	 * the route appropriately, otherwise just update the timer and
	 * set the valid flag.
	 */
	if (! nroute) {
		neighbor_route_add(neigh, nroute, rec, ipstr, hash);
		return;
	}

	/* Is there any difference between the previous and current metrics? */
	diff = nroute->metric - (neigh->distance + rec->metric);
	if (! diff) {

		/* No difference in metric, set valid flag and move on */
		nroute->valid++;
	} else {

		/* Update the neighbor's route with the difference */
		neighbor_route_mod(neigh, nroute, rec, diff);
	}
		
	DBG(DEV_INOUT, "Leaving function.");
	return;
}

/* neighbor_route_mod
 */
void neighbor_route_mod(neighbor * neigh, neighborRoute * nroute,
                        updateRecord * rec, int8_t diff)
{
	internalRoute best;           /* stores best neighbor route */
	neighborRoute * old;

	nroute->metric = rec->metric;

	if (nroute->installed) {
		/* Route was installed, and the... */
		if (diff < 0) {
          		/* ... metric improved */
			return;   
		} else if (best_neighbor_route_to(nroute->hash, &best)) {
			/* ... metric got worse; found a route to this prefix */
			if (best.route != nroute) {
				internal_route_del(nroute->hash);
				internal_route_add(best.neigh, best.route);
			} else {
				return;
			}
		} else {
			/* Route is on holddown */
			DBG(RECV_1, "Ignoring route for prefix on holddown.");
			return;
		}
	} else if (diff < 0) {
		 /* Route was not installed and metric improved */
		if (is_new_best_route(nroute, &old)) {
			if (old) {
				internal_route_del(old->hash);
			}
			internal_route_add(neigh, nroute);
		} else {
			/* Metric got worse */
			return;
		}
	}
}

/* best_neighbor_route_to
 * Find the best route any neighbor has to the given prefix.  
 * Populate `internal' with the reply, or NULL if no neighbor has a path.
 * Return 1 is a route is found for this prefix, otherwise return 0.
 */
_Bool best_neighbor_route_to(uint64_t hash, internalRoute * best)
{
	neighbor * neigh;
	neighborRoute * route;

	best->route = NULL;

	/* Outer loop: go through each neighbor */

	for (neigh = firstNeighbor; neigh != NULL; neigh = neigh->next) {
		/* Inner loop: go through each route for this neighbor */

		if ((route = neighbor_route_lookup(neigh, hash)) != NULL) {
			if (on_holddown(hash))
				return 0;
			if (best->route == NULL) {
				best->neigh = neigh;
				best->route = route;
			} else if (route->metric < best->route->metric) {
				best->route = route;
				best->neigh = neigh;
			}
		}
	}

	if (neigh != NULL)
		return 1;
	else
		return 0;
}

/* neighbor_route_add (XXX - not using nroute argument for anything?)
 * Append the given route to the end of this neighbor's route list.
 * Call necessary functions to see if this route is "good enough" to be
 * installed in the internal routing table (and then the kernel routing table).
 */
void neighbor_route_add(neighbor * neigh, neighborRoute * nroute,
                        updateRecord * rec, char * ipstr, uint64_t hash)
{
	neighborRoute * new, * prev, * last;
	neighborRoute * old = NULL;          /* old best neighbor route */

	if (! (new = malloc(sizeof(neighborRoute)))) {
		DBG(ERR, "Malloc failed. Exiting.");
		pthread_exit(NULL);
	}

	/* Get last route in the route list */
	if (last = get_last_neighbor_route(neigh)) {

		/* Update the former last route's "next" pointer -- "new" is 
		 * now the real last route */
		last->next = new;
		new->prev = last;
	} else {

		/* Neighbor had no routes */
		neigh->firstroute = new;
	}

	/* Populate the new route */
	new->next = NULL;
	new->network = rec->network;
	new->preflen = rec->preflen;
	new->hash = hash;
	new->metric = rec->metric + neigh->distance;
	new->valid = 1;
	strncpy(new->netstr, ipstr, sizeof(new->netstr));

	/* Determine if we should install this route in the internal routing
	 * table (and consequently the kernel routing table). "old" will be
	 * filled in with the previous best route, if one existed.
	 */
	if (is_new_best_route(new, &old)) {
		DBG(RECV_1, "Found new best route to prefix %s/%d via %s in %d."
		    "", new->netstr, new->preflen, neigh->ipstr, new->metric);
		if (old) {
			internal_route_del(old->hash);
		}
		internal_route_add(neigh, new);
	} else {
		DBG(DEV_1, "Route to %s/%d via %s in %d was not the best route "
		    "for this prefix.\n", new->netstr, new->preflen, 
		    neigh->ipstr, new->metric);
	}
	return;
}

/* internal_route_del
 * Remove this route from the linked list and free its memory.
 */
void internal_route_del(uint64_t hash)
{
	internalRoute * internal;

	if (! (internal = lookup_internal_route(hash)))
		return;

	if (internal == firstInternalRoute) {
		DBG(DEV_1, "Deleting firstInternalRoute.");
		firstInternalRoute = internal->next;
	} else {
		DBG(DEV_2, "This is not firstInternalRoute.");
	}

	if (internal == lastInternalRoute) {
		DBG(DEV_1, "Deleting lastInternalRoute.");
		lastInternalRoute = internal->prev;
	} else {
		DBG(DEV_2, "This is not lastInternalRoute.");
	}

	if (internal->prev)
		internal->prev->next = internal->next;
	if (internal->next)
		internal->next->prev = internal->prev;

	internal->route->installed = 0;
	kernel_route(internal, DEL, LOCAL(internal->neigh));
	free(internal);
	return;
}

/* lookup_internal_route
 * Find the internal route to the hashed network. Return the internalRoute
 * structure, or NULL if not found.
 */
internalRoute * lookup_internal_route(uint64_t hash)
{
	internalRoute * curr;

	/* Find the given hash */
	for (curr = firstInternalRoute; 
	     curr != NULL && curr->route->hash != hash; 
	     curr = curr->next);   // one statement

	return curr;
}

/* is_new_best_route
 * See if there is already an installed route to the new route's prefix.  If
 * there is, set "old" equal to it and see if the new route is better than 
 * the existing one. If yes, return true. Else return false.
 * (Ties go to the old route.)
 */
_Bool is_new_best_route(neighborRoute * new, neighborRoute ** old)
{
	internalRoute * internal; 

	if (internal = get_internal_route(new->hash)) {
		/* There was a valid path to this prefix */
		*old = internal->route;
		if ((*old)->metric <= new->metric) {
			DBG(DEV_1, "Old route has BETTER or equal metric.");
			return 0;
		} else {
			DBG(DEV_1, "Old route has WORSE metric.");
			return 1;
		}
	} else {
		/* No other valid paths, winner by default unless holddown is
		 * set for this network */
		*old = NULL;
		if (on_holddown(new->hash)) {
			DBG(RECV_1, "Ignoring route to prefix on holddown.");
			return 0;
		}
		DBG(DEV_1, "New route is the first for this prefix.");
		return 1;
	}
}

internalRoute * get_internal_route(uint64_t hash)
{
	internalRoute * curr;

	for (curr = firstInternalRoute; curr != NULL; curr = curr->next) {
		if (curr->route->hash == hash) {
			return curr;
		}
	}

	/* No route found for this prefix */
	return NULL;
}

/* internal_route_add
 * Add the passed route to the internal and kernel routing tables. 
 */
void internal_route_add(neighbor * neigh, neighborRoute * nr)
{
	internalRoute * new = malloc(sizeof(internalRoute));

	/* Populate the new internal route */
	new->neigh = neigh;
	new->route = nr;
	new->next = NULL;
	new->route->installed = 1;

	/* Add this route to the end of the internal route list */
	if (lastInternalRoute) {
		lastInternalRoute->next = new;
		new->prev = lastInternalRoute;
		lastInternalRoute = new;
	} else {
		firstInternalRoute = lastInternalRoute = new;
		new->prev = NULL;
	}
	
	/* Add to kernel */
	kernel_route(new, ADD, LOCAL(neigh));

	return;
}

/* get_last_neighbor_route
 * Loop through all routes to find this neighbor's last route.
 * XXX - Update to either store the last route as part of the neighbor struct,
 * or use a sorted binary tree to speed things up.
 * Return NULL if neighbor has no routes.
 */
neighborRoute * get_last_neighbor_route(neighbor * neigh)
{
	neighborRoute * prev = NULL, * curr;

	for (curr = neigh->firstroute; curr != NULL; curr = curr->next)
		prev = curr;

	return prev;
}

void process_invalid_route(neighbor * neigh, neighborRoute * nroute)
{
	DBG(DEV_INOUT, "Entered function.");
	holddown_add(nroute->hash);
	neighbor_route_del(neigh, nroute);
	DBG(DEV_INOUT, "Leaving function.");
}

/* neighbor_route_del
 * Delete and free a route from a given neighbor. If it was installed, delete 
 * the internal route.
 */
void neighbor_route_del(neighbor * neigh, neighborRoute * nroute)
{
	if (neigh->firstroute == nroute) {
		DBG(DEV_1, "This is the firstroute.");
		neigh->firstroute = nroute->next;
	} else {
		DBG(DEV_1, "This is not the firstroute.");
		nroute->prev->next = nroute->next;
	}

	if (nroute->next)
		nroute->next->prev = nroute->prev;
	if (nroute->installed) {
		internal_route_del(nroute->hash);
		DBG(DEV_1, "Ok.");
	}

	/* Don't try to install a new route; this route is going on holddown */
	free(nroute);
	return;
}

/* neighbor_route_lookup
 * Determine if the given neighbor has a route to the given hash.
 * Return the route structure if he does, otherwise return NULL.
 */
neighborRoute * neighbor_route_lookup(neighbor * neighbor, uint64_t hash)
{
	neighborRoute * cr;     /* current route */

	DBG(DEV_INOUT, "Entered function.");
	DBG(DEV_2, "Looking up hash %llu.", hash);
	for (cr = neighbor->firstroute; cr != NULL; cr = cr->next) {
		if (cr->hash == hash) {
			DBG(DEV_INOUT, "Leaving function.");
			return cr;
		}
	}

	/* Route not found */

	return NULL;

	DBG(DEV_INOUT, "Leaving function.");
}

/* update_metric
 * Currently just increments the metric by one and returns it. Feel free to
 * play around with this. You may want to pass the interface index of this
 * neighbor into here in order to do more advanced decision making.
 */
uint8_t update_metric(updateRecord * rec)
{
	return rec->metric + 1;
}

/* update_neighbor
 * Update the neighbor's timestamp and distance.
 * For now, the distance is always 1 (one hop away).
 */
void update_neighbor(neighbor * neigh)
{
	DBG(DEV_INOUT, "Entered function.");

	neigh->lastheard = time(NULL);
	DBG(DEV_2, "Updating neighbor lastheard to: %u", neigh->lastheard);
	neigh->distance = 1;

	DBG(DEV_INOUT, "Leaving function.");
	return;
}

/* is_local
 * Is the given IP address assigned to me?
 * If yes, return 1, otherwise return 0.
 */
_Bool is_local(in_addr_t addr)
{
	interface * cif = firstInterface;   /* Current interface */

	DBG(DEV_INOUT, "Entering function.");
	while (cif != NULL) {
		if (cif->ip == addr) {
			DBG(RECV_3, "Address belongs to %s", cif->ifname);
			DBG(DEV_INOUT, "Leaving function.");
			return 1;
		} else {
			cif = cif->next;
		}
	}
	/* Address not assigned to any interface */

	DBG(DEV_INOUT, "Leaving function.");
	return 0;
}

/* lookup_neighbor: Go through the neighbor list from start to finish, 
 * see if we find this IP address.  If we do, this is an update from an 
 * existing neighbor.  Otherwise, it's a new neighbor.
 * Return the neighbor's neighbor structure if it exists, otherwise
 * return NULL.
 * XXX - Update to do a qsort/binary search operation at some point. 
 */
neighbor * lookup_neighbor(in_addr_t addr)
{
	neighbor * cneigh;  /* Current neighbor */

	DBG(DEV_INOUT, "Entered function.");

	cneigh = firstNeighbor;
	while (cneigh != NULL) {
		if (cneigh->addr == addr) {
			DBG(RECV_1, "Neighbor %s found.", cneigh->ipstr);
			DBG(DEV_INOUT, "Leaving function.");
			return cneigh;
		} else {
			cneigh = cneigh->next;
		}
	}
	/* Only reached if neighbor does not yet exist */
	
	DBG(RECV_1, "Neighbor not found.");
	DBG(DEV_INOUT, "Leaving function.");
	return NULL;
}

/* create_neighbor
 * Create a new neighbor at IP `addr'.
 * If this is the first neighbor, set the global "first neighbor" accordingly,
 * and signal the verify thread to wake up and start doing its thing.
 * We accept the previous neighbor as an argument, though at this time it will
 * be the global variable "lastNeighbor".  This is to ease in changing to a more
 * efficient way of adding/looking up neighbors.
 */
neighbor * create_neighbor(in_addr_t addr, neighbor * prev)
{
	neighbor * neigh = malloc(sizeof(neighbor));
	char ifstr[IFNAMSIZ];

	DBG(DEV_INOUT, "Entered function.");

	/* Populate the new neighbor's neighbor structure */
	neigh->addr = addr;
	inet_ntop(AF_INET, &addr, neigh->ipstr, sizeof(neigh->ipstr));
	neigh->lastheard = 0;
	neigh->iface = get_interface(addr);
	neigh->next = NULL;
	neigh->prev = prev;
	neigh->firstroute = NULL;

	if (! firstRemoteNeighbor) {
		DBG(RECV_2, "Assigning new first neighbor.");
		firstRemoteNeighbor = neigh;
		firstNeighbor->next = firstRemoteNeighbor;
		firstRemoteNeighbor->prev = firstNeighbor;

		/* Wake up the verify thread -- we have company */
		pthread_mutex_lock(&mutexHaveNeighbor);
		haveNeighbor = 1;
		pthread_cond_signal(&condNeighbor);
		pthread_mutex_unlock(&mutexHaveNeighbor);
	} else {
		lastNeighbor->next = neigh;
	}

	/* Update lastNeighbor */
	lastNeighbor = neigh;

	DBG(RECV_1, "Neighbor IP: %s, interface: %s", neigh->ipstr,
	    if_indextoname(neigh->iface->ifnumber, ifstr));
	DBG(DEV_INOUT, "Leaving function.");
	return neigh;
}

/* get_interface:  Return a pointer to the interface this address is on,
 * assuming it is a broadcast from a peer connected to the same subnet 
 * as us.  Return NULL if the interface is not found. 
 */
interface * get_interface(in_addr_t addr)
{
	interface * iface = firstInterface;

	while (iface != NULL) {
		if (iface->network == (addr & iface->mask)) {
			DBG(DEV_2, "%u equals %u",
			    iface->network, addr & iface->mask);
			DBG(DEV_1, "Interface #%d matches.", 
			    iface->ifnumber);
			return iface;
		} else {
			DBG(DEV_2, "%u does NOT equal %u", iface->network,
			    addr & iface->mask);
			iface = iface->next;
		}
	}
	
	/* should never be reached */

	DBG(ERR, "Packet received from unknown interface.");
	return NULL;
}
